---
title: GetUserGroups
summary: Returns the complete transitive authorization-group list for a named user.
status: draft
owner: "Not determined from source"
last_reviewed: 2026-09-22
---

# GetUserGroups (OP-13)

## Request and response

| Part | Type | Description |
| --- | --- | --- |
| `inUserId` | `String` | The `SamAccountName` to look up. |
| `inExternalDomain` | `Boolean` | Selects `external.saif.com` when `True`, `corp.saif.com` when `False` ([BR-07](../business-rules.md#br-07-the-directory-domain-is-selected-by-the-caller)). |

| Part | Type | Description |
| --- | --- | --- |
| `<return>` | `ArrayList` | Group-name strings, untyped on the wire (see [Tech debt](../tech-debt.md) — TD-05). Empty on any failure or if the user has no groups. |

## WebMethod attributes

`<WebMethod()>` — bare, no arguments supplied.

## Steps

1. No access check is performed ([BR-06](../business-rules.md#br-06-read-only-directory-queries-are-not-access-controlled)).
2. Create an empty `ArrayList`.
3. In a `Try` block, call H2 `GetUserPrincipal` with the caller-supplied `inExternalDomain`.
4. If the principal is found, iterate the **transitive** membership from `userPrin.GetAuthorizationGroups()` and add each `grp.Name` (`String`) to the list ([BR-28](../business-rules.md#br-28-the-group-list-includes-nested-membership)).
5. `Catch`: **empty block** — every failure (unresolved user, directory exception) is silently swallowed and yields an empty `ArrayList` that is indistinguishable from "user has no groups" (tech debt TD-21, HIGH — see [Tech debt](../tech-debt.md)).
6. `Finally`: release `userPrin`.
7. Return the list.

## Process flow

```mermaid
flowchart TD
    A["Call GetUserGroups"] --> B["GetUserPrincipal<br>caller-selected domain"]
    B -->|"Nothing"| C["Empty list unchanged"]
    B -->|"Found"| D["Iterate GetAuthorizationGroups<br>transitive membership"]
    D --> E["Add each grp.Name<br>to ArrayList"]
    B -->|"Exception"| F["Empty Catch block<br>swallows failure"]
    C --> G["Return ArrayList"]
    E --> G
    F --> G
```

## Shared state effects

None — this operation touches no process-wide state; all locals are per-request.

## Error handling

This operation never faults. Every failure path — an unresolved user or a directory exception in the empty `Catch` block — returns the empty `ArrayList` accumulated so far, with no way for the caller to distinguish "no groups" from "the lookup failed" (TD-21, see [Tech debt](../tech-debt.md) and [Faults](../faults.md) for the service-wide fault model).

## Authorization

None in code. This is one of the eleven ungated operations, and specifically the one that returns the complete transitive authorization-group list for any named user with no requirement that the caller be authenticated (SEC-15, see [Authentication and authorization](../authentication-authorization.md) and [Tech debt](../tech-debt.md)).

## Related documentation
- [Service index](../index.md)
- [Overview](../overview.md)
- [Architecture](../architecture.md)
- [Activation and hosting](../activation-hosting.md)
- [Operations](../operations.md)
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)
- [Authentication and authorization](../authentication-authorization.md)
- [Configuration](../configuration.md)
- [Dependencies](../dependencies.md)
- [Faults](../faults.md)
- [Operational runbook](../operational-runbook.md)
- [Tech debt](../tech-debt.md)

## Sources
- `src\ADUserServices\clsADUserServices.asmx.vb` (lines 556-580)
